#!/bin/env python

# gmailsmtp.py -- Demo to allow smtp delivery via Gmail
# $Revision: 1.5 $ ($Date: 2006/11/30 05:08:13 $)
#
# Author: follower@myrealbox.com
#
# License: GPL 2.0
#
# Changed History: 
#		
#	2006-11-30, by Parvel:
#	1. Nested MIME part support, avoid the uncaptured exception while message/rfc822
#	2. Enable html format in message, with libgmail modified
#	3. Charset other than default(ascii) support
#	4. Extract from-to header in original MIME parts
#
#	2007-02-07, by Parvel:
#	1. Fix the CLOSE_WAIT socket leak;

#	$Id$

import os
import sys
import email
import base64
import asyncore
import logging
import smtpd
import parse_dump

# Allow us to run using installed `libgmail` or the one in parent directory.
try:
    import libgmail
except ImportError:
    # Urghhh...
    sys.path.insert(1,
                    os.path.realpath(os.path.join(os.path.dirname(__file__),
                                                  os.path.pardir)))

    import libgmail

g_forward_addr = ''
g_is_log_on = True
g_log_level = logging.DEBUG
g_log_filename = './gmailsmtp.log'

ga = None

class GmailSmtpProxy(smtpd.SMTPServer):
    """
    """

    def process_message(self, peer, mailfrom, rcpttos, data):
        """
        """
        result = None

        body = ""
        attachments = []
        ext_paras = {
		'ishtml':'0',
		'from' : ''
	}
        msg = email.message_from_string(data)

        #import pdb; pdb.set_trace()

        # Handle attachments, if present.
	if msg.has_key('From'):
		from_addr = msg.get('From')
		#ext_paras['from'] = from_addr
		if g_is_log_on:
			logging.info('----Incoming Mail----')
			logging.info('From: %s' % from_addr)
		
		#save the original from-to header
		body += extract_from_header(msg)
		#replace with our forward gmail
		replace_destination(msg, g_forward_addr)
		
        if msg.is_multipart():
		for part in msg.walk():
			body += extract_from_header(part)	
			if part.get_content_maintype() == 'text' and (None == part.get_filename()):
				body += part.get_payload(decode=True)
				if part.get_content_type() == 'text/html':
					ext_paras['ishtml'] = '1'
			else:
				attachments.append(part)
        else:
		body += msg.get_payload(decode=True)
		if msg.get_content_type() == 'text/html':
			ext_paras['ishtml'] = '1'

        gmsg = libgmail.GmailComposedMessage(to = msg["To"],
                                             subject = msg["Subject"],
                                             body = body,
                                             files = attachments)
	gmsg.encoding = get_charset_encoding(msg)

        # Don't drop connection until we know we delivered...
        if not ga.sendMessage(gmsg, _extraParams=ext_paras):
            result = "Could not deliver."

        return result


    def handle_accept(self):
        conn, addr = self.accept()
        print >> smtpd.DEBUGSTREAM, 'Incoming connection from %s' % repr(addr)
        channel = ESMTPChannel(self, conn, addr)


def extract_from_header(part):
	body = ''
	if part.has_key('From'):
		body += '\n<br>----Forward Mail----\n<br>'
		body += 'From: %s\n<br>' % part.get('From').replace('<', '').replace('>', '')
	if part.has_key('To'):
		body += 'To: %s\n<br>' % part.get('To').replace('<', '').replace('>', '')
	if part.has_key('Cc'):
		body += 'Cc: %s\n<br>' % part.get('Cc').replace('<', '').replace('>', '')
	return body

def replace_destination(msg, dest=''):
	if msg.has_key('To'):
		msg.replace_header('To', dest)
	if msg.has_key('Cc'):
		msg.replace_header('Cc', '')
	if msg.has_key('Bcc'):
		msg.replace_header('Bcc', '')
	

def get_charset_encoding(msg):
	encoding = ''
	p = msg.get_params([],)
	for t in p:
		if 'charset' == t[0]:
			encoding = t[1]
			break
	if g_is_log_on:
		logging.debug('''charset=%s''' % encoding)
	return encoding
	
def set_logger():
        logging.basicConfig(
	level = g_log_level, 
	format='%(asctime)s %(levelname)-8s %(message)s',
        datefmt='%a, %d %b %Y %H:%M:%S', 
	filename= g_log_filename, 
	filemode='a')
	parse_dump.logger = logging

						

class ESMTPChannel(smtpd.SMTPChannel):
    """
    """
    
    def smtp_EHLO(self, arg):
        if not arg:
            self.push('501 Syntax: EHLO hostname')
            return
##        if self.__greeting:
        if self._SMTPChannel__greeting:
            self.push('503 Duplicate HELO/EHLO')
        else:
##             self.__greeting = arg
##             self.push('250 %s' % self.__fqdn)
            self._SMTPChannel__greeting = arg
            self.push('250-%s' % self._SMTPChannel__fqdn)
            self.push('250 AUTH PLAIN')


    def smtp_AUTH(self, arg):
        """
        """
        kind, data = arg.split(" ")
        # TODO: Ensure kind == "PLAIN"
        # TODO: Support "LOGIN" (required by Outlook?) <http://www.technoids.org/saslmech.html>
	    #print 'login arg recieved: %s, %s' % (str(kind), str(data))
        data = base64.decodestring(data)[1:]
        user, pw = data.split("\x00")
        #logging.debug("user:%s, pw:%s"%(user, pw))
        global ga
        ga = libgmail.GmailAccount(user, pw)
        
        try:
            ga.login()
        except libgmail.GmailLoginFailure:
            self.push("535 Authorization failed")
        else:
            self.push('235 Ok')


if __name__ == "__main__":

    #smtpd.DEBUGSTREAM = sys.stderr
    argc = len(sys.argv)
    if argc > 1:
    	g_forward_addr = str(sys.argv[1])
	set_logger()
    	server = GmailSmtpProxy(("localhost", 8025), None)
	asyncore.loop()
